package state

type LuaType int
type ArithOp int
type CompareOp = int

type luaState struct {
	registry *LuaTable
	stack    *LuaStack
}

const (
	LUA_MINSTACK            = 20
	LUAI_MAXSTACK           = 1000000
	LUA_REGISTRYINDEX       = -LUAI_MAXSTACK - 1000
	LUA_RIDX_GLOBALS  int64 = 2
)

type GoFunction func(LuaState) int

func NewLuaState() *luaState {
	registry := NewLuaTable(0, 0)
	registry.Put(LUA_RIDX_GLOBALS, NewLuaTable(0, 0))
	ls := &luaState{registry: registry}
	ls.PushLuaStack(NewLuaStack(LUA_MINSTACK, ls))
	return ls
}

type LuaState interface {
	//stack manipulation
	GetTop() int
	AbsIndex(idx int) int
	CheckStack(n int) bool
	Pop(n int)
	Copy(fromIdx, toIdx int)
	PushValue(idx int)
	Replace(idx int)
	Insert(idx int)
	Remove(idx int)
	Rotate(idx, n int)
	SetTop(idx int)
	// access functions
	TypeName(tp LuaType) string
	Type(idx int) LuaType
	IsNone(idx int) bool
	IsNil(idx int) bool
	IsNoneOrNil(idx int) bool
	IsBoolean(idx int) bool
	IsInteger(idx int) bool
	IsNumber(idx int) bool
	IsString(idx int) bool
	ToBoolean(idx int) bool
	ToInteger(idx int) int64
	ToIntegerX(idx int) (int64, bool)
	ToNumber(idx int) float64
	ToNumberX(idx int) (float64, bool)
	ToString(idx int) string
	ToStringX(idx int) (string, bool)
	//push functions
	PushNil()
	PushBoolean(b bool)
	PushInteger(n int64)
	PushNumber(n float64)
	PushString(s string)

	Arith(op ArithOp)
	Compare(idx1, idx2 int, op CompareOp) bool
	Len(idx int)  //# 取长度运算
	Concat(n int) // 字符串拼接
	PrintStack()

	NewTable()
	CreateTable(nArr, nRec int)
	GetTable(idx int) LuaType
	GetField(idx int, k string) LuaType
	GetI(idx int, i int) LuaType
	SetTable(idx int)
	SetField(idx int, k string)
	SetI(idx int, n int64)

	Load(chunk []byte, chunkName, mode string) int
	Call(nArgs, nResults int)

	//GoFunction 相关
	PushGoFunction(function GoFunction)
	IsGoFunction(idx int) bool
	ToGoFunction(idx int) GoFunction

	//全局环境
	PushGlobalTable()
	GetGlobal(name string) LuaType
	SetGlobal(name string)
	Register(name string, f GoFunction)

	PushGoClosure(f GoFunction, n int)
}

func (this *luaState) PushGoFunction(goFunc GoFunction) {
	this.stack.push(NewGoClosure(goFunc, 0))
}
func (this *luaState) IsGoFunction(idx int) bool {
	val := this.stack.get(idx)
	f, ok := val.(*closure)
	return ok && f.goFunc != nil
}
func (this *luaState) ToGoFunction(idx int) GoFunction {
	val := this.stack.get(idx)
	if f, ok := val.(*closure); ok {
		return f.goFunc
	}
	return nil
}

func (this *luaState) PushGlobalTable() {
	global := this.registry.Get(LUA_RIDX_GLOBALS)
	this.stack.push(global)
}

/**
取出变量表中的值，放到栈上
*/
func (this *luaState) GetGlobal(name string) LuaType {
	t := this.registry.Get(LUA_RIDX_GLOBALS)
	return this.getTable(t, name)
}

func (this *luaState) SetGlobal(name string) {
	t := this.registry.Get(LUA_RIDX_GLOBALS)
	v := this.stack.pop()
	this._setTable(t, name, v)
}

/**
全局环境注册Go函数值
*/
func (this *luaState) Register(name string, f GoFunction) {
	this.PushGoFunction(f)
	this.SetGlobal(name)
}

func (this *luaState) PushGoClosure(f GoFunction, n int) {
	c := NewGoClosure(f, n)
	for i := n; i > 0; i-- {
		val := this.stack.pop()
		c.upvalues[n-1] = &upvalue{&val}
	}
	this.stack.push(c)
}

func LuaValueIndex(i int) int {
	return LUA_REGISTRYINDEX - i
}
